/*******************************************************************************
 * Copyright (c) 2008, 2015 Wind River Systems and others.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html
 * 
 * Contributors:
 *     Wind River Systems - initial API and implementation
 *     Winnie Lai (Texas Instruments) - Individual Element Number Format (Bug 202556)
 *******************************************************************************/
package org.eclipse.cdt.tests.dsf.vm;

import java.util.Iterator;
import java.util.Set;

import org.eclipse.cdt.dsf.concurrent.CountingRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.DataRequestMonitor;
import org.eclipse.cdt.dsf.concurrent.RequestMonitor;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.IDebugVMConstants;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueLabelText;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.numberformat.FormattedValueRetriever;
import org.eclipse.cdt.dsf.debug.ui.viewmodel.update.ElementFormatEvent;
import org.eclipse.cdt.dsf.service.DsfSession;
import org.eclipse.cdt.dsf.ui.viewmodel.IRootVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.VMDelta;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMNode;
import org.eclipse.cdt.dsf.ui.viewmodel.datamodel.AbstractDMVMProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IElementPropertiesProvider;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.IPropertiesUpdate;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelAttribute;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelColumnInfo;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.LabelText;
import org.eclipse.cdt.dsf.ui.viewmodel.properties.PropertiesBasedLabelProvider;
import org.eclipse.cdt.tests.dsf.ViewerUpdatesListener;
import org.eclipse.cdt.tests.dsf.vm.TestModel.TestElement;
import org.eclipse.cdt.tests.dsf.vm.TestModel.TestEvent;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenCountUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IElementLabelProvider;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IHasChildrenUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.ILabelUpdate;
import org.eclipse.debug.internal.ui.viewers.model.provisional.IModelDelta;
import org.eclipse.jface.util.PropertyChangeEvent;

/**
 * @since 2.2
 */
public class TestModelDMVMNode extends AbstractDMVMNode implements IRootVMNode, IElementLabelProvider, IElementPropertiesProvider {

    final public static String PROP_PREFIX_DUMMY = "dummy.";
    
    final private static String PROP_TEST_ELEMENT_LABEL = "PROP_TEST_ELEMENT_LABEL";
    
    private ViewerUpdatesListener fViewerUpdateListener = NULL_VIEWER_UPDATE_LISTENER; 
    private FormattedValuesListener fFormattedValuesListener;
    
    private static final ViewerUpdatesListener NULL_VIEWER_UPDATE_LISTENER = new ViewerUpdatesListener();
        
    final private static PropertiesBasedLabelProvider fLabelProvider = new PropertiesBasedLabelProvider();
    {
        LabelColumnInfo idLabelInfo = new LabelColumnInfo(new LabelAttribute[] { 
            new LabelText("{0}", new String[] { PROP_TEST_ELEMENT_LABEL })
        });  
        
        fLabelProvider.setColumnInfo(PropertiesBasedLabelProvider.ID_COLUMN_NO_COLUMNS, idLabelInfo); 
        fLabelProvider.setColumnInfo(TestModelCachingVMProvider.COLUMN_ID, idLabelInfo);
        fLabelProvider.setColumnInfo(
            TestModelCachingVMProvider.COLUMN_FORMATTED_VALUE, 
            new LabelColumnInfo(new LabelAttribute[] {
                new FormattedValueLabelText()
            }));
        fLabelProvider.setColumnInfo(
            TestModelCachingVMProvider.COLUMN_DUMMY_VALUE, 
            new LabelColumnInfo(new LabelAttribute[] {
                new FormattedValueLabelText(PROP_PREFIX_DUMMY)
            }));
    }

    private final FormattedValueRetriever fFormattedValueRetriever;
    private final FormattedValueRetriever fDummyFormattedValueRetriever;

    public TestModelDMVMNode(AbstractDMVMProvider provider, DsfSession session) {
        super(provider, session, TestElement.class);
        fFormattedValueRetriever = new FormattedValueRetriever(this, getSession(), TestModel.class, TestElement.class);
        fDummyFormattedValueRetriever = new FormattedValueRetriever(this, getSession(), DummyFormattedValueService.class, TestElement.class, PROP_PREFIX_DUMMY);
    }

    @Override
    public void dispose() {
        super.dispose();
        fFormattedValueRetriever.dispose();
        fDummyFormattedValueRetriever.dispose();
    }
    
    public void setVMUpdateListener(ViewerUpdatesListener viewerUpdateListener) {
        if (viewerUpdateListener != null) {
            fViewerUpdateListener = viewerUpdateListener;
        } else {
            fViewerUpdateListener = NULL_VIEWER_UPDATE_LISTENER;
        }
    }

    public void setFormattedValuesListener(FormattedValuesListener formattedValuesListener) {
        fFormattedValuesListener = formattedValuesListener;
    }
    
    public PropertiesBasedLabelProvider getLabelProvider() {
        return fLabelProvider;
    }
    
    @Override
    public void update(final ILabelUpdate[] updates) {
        fLabelProvider.update(updates);
    }
        
    @Override
    public void update(IHasChildrenUpdate[] updates) {
        fViewerUpdateListener.viewerUpdatesBegin();
        for (IHasChildrenUpdate update : updates) {
            fViewerUpdateListener.updateStarted(update);
            if (update.getElement() instanceof TestElementVMContext) {
                TestElement element = ((TestElementVMContext)update.getElement()).getElement();
                update.setHasChilren(element.getChildren().length != 0);
            }
            update.done();
            fViewerUpdateListener.updateComplete(update);
        }
        fViewerUpdateListener.viewerUpdatesComplete();
    }

    @Override
    public void update(IChildrenCountUpdate[] updates) {
        fViewerUpdateListener.viewerUpdatesBegin();
        for (IChildrenCountUpdate update : updates) {
            fViewerUpdateListener.updateStarted(update);
            if (update.getElement() instanceof TestElementVMContext) {
                TestElement element = ((TestElementVMContext)update.getElement()).getElement();
                update.setChildCount(element.getChildren().length);
            }
            update.done();
            fViewerUpdateListener.updateComplete(update);
        }
        fViewerUpdateListener.viewerUpdatesComplete();
    }

    @Override
    public void update(IChildrenUpdate[] updates) {
        fViewerUpdateListener.viewerUpdatesBegin();
        for (IChildrenUpdate update : updates) {
            fViewerUpdateListener.updateStarted(update);
            if (update.getElement() instanceof TestElementVMContext) {
                TestElement element = ((TestElementVMContext)update.getElement()).getElement();
                fillUpdateWithTestElements(update, element.getChildren());
            }
            update.done();
            fViewerUpdateListener.updateComplete(update);
        }        
        fViewerUpdateListener.viewerUpdatesComplete();
    }
    
    
    @Override
    protected void updateElementsInSessionThread(IChildrenUpdate update) {
        // TODO Auto-generated method stub
        
    }
    
    @Override
    public void update(final IPropertiesUpdate[] updates) {
        fViewerUpdateListener.propertiesUpdatesStarted(updates);
        if (fFormattedValuesListener != null) fFormattedValuesListener.propertiesUpdatesStarted(updates);
        
        CountingRequestMonitor crm = new CountingRequestMonitor(getExecutor(), null) {
            @Override
            protected void handleSuccess() {
                for (IPropertiesUpdate update : updates) {
                    if (update.getElement() instanceof TestElementVMContext) {
                        TestElement element = ((TestElementVMContext)update.getElement()).getElement();
                        update.setProperty(PROP_TEST_ELEMENT_LABEL, element.getLabel());
                    }
                    update.done();
                    fViewerUpdateListener.propertiesUpdateCompleted(update);
                    if (fFormattedValuesListener != null) fFormattedValuesListener.propertiesUpdateCompleted(update);
                }
            }
        };
        int count = 0;
        
        fFormattedValueRetriever.update(updates, crm);
        count++;
        fDummyFormattedValueRetriever.update(updates, crm);
        count++;
        crm.setDoneCount(count);
    }
    
    private void fillUpdateWithTestElements(IChildrenUpdate update, TestElement[] modelElements) {
        int updateIdx = update.getOffset() != -1 ? update.getOffset() : 0;
        int endIdx = updateIdx + (update.getLength() != -1 ? update.getLength() : modelElements.length);
        while (updateIdx < endIdx && updateIdx < modelElements.length) {
            update.setChild(createVMContext(modelElements[updateIdx]), updateIdx);
            updateIdx++;
        }
    }
    
    public TestElementVMContext createVMContext(TestElement element) {
        return new TestElementVMContext(this, element);
    }

    /*
     * (non-Javadoc)
     * @see org.eclipse.cdt.dsf.ui.viewmodel.IVMNode#getDeltaFlags(java.lang.Object)
     */
    @Override
    public int getDeltaFlags(Object e) {
        if ( e instanceof PropertyChangeEvent &&
             ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) 
        {
            return IModelDelta.CONTENT;
        } 
        if ( e instanceof ElementFormatEvent) 
        {
            return IModelDelta.STATE;
        }         
        if (e instanceof TestEvent) {
            return ((TestEvent)e).getType();
        }
        
        return IModelDelta.NO_CHANGE;
    }

    @Override
    public void buildDelta(Object e, VMDelta parent, int nodeOffset, RequestMonitor rm) {
        if ( e instanceof PropertyChangeEvent &&
            ((PropertyChangeEvent)e).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) 
        {
            parent.setFlags(parent.getFlags() | IModelDelta.CONTENT);
        } 
        if ( e instanceof ElementFormatEvent) 
        {
        	Set<Object> elements = ((ElementFormatEvent) e).getElements();
        	Iterator<Object> it = elements.iterator();
        	while (it.hasNext()) {
        		parent.addNode(it.next(), IModelDelta.STATE);
        	}
        } 
        rm.done();
    }


    @Override
    public boolean isDeltaEvent(Object rootObject, Object event) {
        return getDeltaFlags(event) != IModelDelta.NO_CHANGE;
    }

    @Override
    public void createRootDelta(Object rootObject, Object event, DataRequestMonitor<VMDelta> rm) {
        int flags = IModelDelta.NO_CHANGE;
        if ( event instanceof PropertyChangeEvent &&
            ((PropertyChangeEvent)event).getProperty() == IDebugVMConstants.PROP_FORMATTED_VALUE_FORMAT_PREFERENCE) 
        {
            flags |= IModelDelta.CONTENT;
        } 
        if ( event instanceof ElementFormatEvent) 
        {
            flags |= IModelDelta.CONTENT;
        } 
        
        // TODO: make more sophisticated to update specific elements.
        if (event instanceof TestEvent) {
            flags|= ((TestEvent)event).getType();
        }
        
        rm.setData( new VMDelta(rootObject, 0, flags) );
        rm.done();
    }


}
